import path from 'path';
import fs from 'fs';

import express from 'express';
import dotenv from 'dotenv';
import bodyParser from 'body-parser';
import cors from 'cors';
import nunjucks from 'nunjucks';

import { connectMongoDB } from './db';
import { loggerMiddleware } from './middlewares/logger.middleware';
import { errorMiddleware } from './middlewares/error.middleware';

import { ControllerInterface } from './interfaces/controller.interface';

import { getIPAddress } from './helpers/common';

export class App {
  public app: express.Application;

  constructor(controllers: Array<ControllerInterface>) {
    this.app = express();

    this.initEnvConfig();
    this.initMiddleWaresBeforeControllers();
    this.initControllers(controllers);
    this.initMiddleWaresAfterControllers();
    this.initDataBase();
  }

  public listen() {
    const PORT = process.env.PORT;
    this.app.listen(PORT, (err) => {
      if (err) {
        console.log(err);
      } else {
        let ip = getIPAddress();
        console.log(`server is running at http://localhost:${PORT}`);
        console.log(`server is running at http://${ip}:${PORT}`);
      }
    });
  }

  private initEnvConfig () {
    const result = dotenv.config();
    if (result.error) {
      throw new Error('dotenv config error');
    }

    const NODE_ENV = process.env.NODE_ENV.toUpperCase();

    const envConfig = dotenv.parse(
      fs.readFileSync(path.resolve(__dirname, `../.env.${NODE_ENV.toLowerCase()}`))
    );

    for(let key in envConfig) {
      if (envConfig.hasOwnProperty(key)) {
        process.env[key] = envConfig[key];
      }
    }
  }

  private initMiddleWaresBeforeControllers() {
    this.app.use(cors());

    this.app.use(bodyParser.urlencoded({ extended: true }));

    this.app.use(bodyParser.json());

    this.app.use(loggerMiddleware.normalLogger);

    this.app.use(express.static(path.resolve(__dirname, './static')));

    this.app.set('views', path.join(__dirname, './views'));

    this.app.set('view engine', 'njk');

    nunjucks.configure(path.join(__dirname, './views'), {
      autoescape: true,
      express: this.app,
      watch: true,
      noCache: true
    });
  }

  private initControllers(controllers: ControllerInterface[]) {
    controllers.forEach(controller => {
      this.app.use('/', controller.router);
    });
  }

  private initMiddleWaresAfterControllers() {
    this.app.use(loggerMiddleware.errorLogger);
    this.app.use(errorMiddleware);
  }

  private async initDataBase () {
    await connectMongoDB();
  }

}
